/*
 * Copyright © 2002-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections;
using System.Runtime.InteropServices;

using NUnit.Framework;

using Spring.Objects;
using Spring.Util;

namespace Spring.Proxy
{
    /// <summary>
    /// A base set of unit tests for the various AbstractProxyTypeBuilder subclasses.
    /// </summary>
    /// <author>Rick Evans</author>
    /// <author>Bruno Baia</author>
    public abstract class AbstractProxyTypeBuilderTests
    {
        [Test]
        public void GeneratesProxyNameIfNoneExplicitlySupplied()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            Assert.IsNotNull(builder.Name);
        }

        [Test]
        public void DoesNotGenerateProxyNameIfOneIsExplicitlySupplied()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.Name = "Bing";
            Assert.AreEqual("Bing", builder.Name);
        }

        [Test]
        public void AppliesAttributeToType()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(MarkerClass);
            builder.TypeAttributes = new Attribute[] { new MarkerAttribute() };

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object[] atts = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(atts, "Should have had 1 custom attribute applied to the generated proxy type.");
            Assert.AreEqual(1, atts.Length, "Should have had 1 custom attribute applied to the generated proxy type.");
            Assert.AreEqual(typeof(MarkerAttribute), atts[0].GetType(), "Wrong System.Type of Attribute applied to the generated proxy type.");
        }

        [Test]
        public void AppliesStarredMemberAttributesToAllMethods()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(TargetObjectTest);
            IDictionary atts = new Hashtable();
            string methodPrefix = typeof(ITargetObjectTest).FullName + ".";
            atts.Add("*", new Attribute[] { new MarkerAttribute() });
            builder.MemberAttributes = atts;
            Type proxy = builder.BuildProxyType();
            object foo = Activator.CreateInstance(proxy);

            Type fooType = foo.GetType();
            MethodInfo method = ReflectionUtils.GetMethod(fooType, methodPrefix + "InterfaceMethodWithArguments", new Type[] { typeof(string) });
            object[] appliedAttributes = method.GetCustomAttributes(false);
            Assert.AreEqual(1, appliedAttributes.Length,
                "Custom attribute not applied to member method.");
            method = ReflectionUtils.GetMethod(fooType, methodPrefix + "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes);
            appliedAttributes = method.GetCustomAttributes(false);
            Assert.AreEqual(1, appliedAttributes.Length,
                "Custom attribute not applied to member method.");
        }

        [Test]
        public void AppliesSpecificMemberAttributesToSpecificMethodOnly()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(TargetObjectTest);
            IDictionary atts = new Hashtable();
            atts.Add("InterfaceVirtualMethodWithNoArguments", new Attribute[] { new MarkerAttribute() });
            builder.MemberAttributes = atts;

            Type proxy = builder.BuildProxyType();
            object foo = Activator.CreateInstance(proxy);

            Type fooType = foo.GetType();
            MethodInfo method = ReflectionUtils.GetMethod(fooType, "InterfaceMethodWithArguments", new Type[] { typeof(string) });
            if (method == null)
            {
                method = ReflectionUtils.GetMethod(fooType, "Spring.Proxy.ITargetObjectTest.InterfaceMethodWithArguments", new Type[] { typeof(string) });
            }
            object[] appliedAttributes = method.GetCustomAttributes(false);
            Assert.AreEqual(0, appliedAttributes.Length,
                "Custom attribute (erroneously) applied to member method.");
            method = ReflectionUtils.GetMethod(fooType, "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes);
            if (method == null)
            {
                method = ReflectionUtils.GetMethod(fooType, "Spring.Proxy.ITargetObjectTest.InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes);
            }
            appliedAttributes = method.GetCustomAttributes(false);
            Assert.AreEqual(1, appliedAttributes.Length,
                "Custom attribute not applied to member method.");
        }

        [Test]
        // SPRNET-1262
        public void AppliesSpecificTypeAttributeWithNoPropertySettersToTargetTypeUsingCustomAttributeBuilder()
        {
            ConstructorInfo ci = typeof(FakeServiceKnownTypeAttribute).GetConstructor(new Type[] { typeof(Type) });
            CustomAttributeBuilder cab = new CustomAttributeBuilder(ci, new object[] { typeof(TestObject) });

            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithFakeServiceKnownTypeAttribute);
            builder.TypeAttributes.Add(cab);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(typeof(FakeServiceKnownTypeAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

            FakeServiceKnownTypeAttribute fgsta = attrs[0] as FakeServiceKnownTypeAttribute;
            Assert.AreEqual(typeof(TestObject), fgsta.Type, "Property 'Type' on Target Attribute not set!");
        }

        [Test]
        public void ImplementsInterfaceHierarchy()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(MultipleInterfaces);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object foo = Activator.CreateInstance(proxy);
            Assert.IsTrue(foo is IBase);
            Assert.IsTrue(foo is IInherited);

            // try to call proxied interface methods
            ((IBase)foo).Base();
            ((IInherited)foo).Base();
            ((IInherited)foo).Inherited();
        }

        [Test]
        public void ProxySpecificTargetTypeAttributeWithReadOnlyProperty()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithGuidAttribute);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(typeof(GuidAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

            GuidAttribute ga = attrs[0] as GuidAttribute;
            Assert.AreEqual("7cfc9607-e81a-48ac-a080-bda88193607b", ga.Value);
        }

        [Test]
        public void ProxySpecificTargetTypeAttributeWithPropertySetterChangingDefaultBehavior()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithFakeXmlElementAttribute);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetParameters()[0].GetCustomAttributes(false);
            Assert.IsNotNull(method);
            Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(typeof(FakeXmlElementAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

            FakeXmlElementAttribute xea = attrs[0] as FakeXmlElementAttribute;
            Assert.AreEqual(-1, xea.Order);
        }

        [Test]
        public void ProxySpecificTargetTypeAttributeNotInstantiableWithDefaultValues()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithFakeGenerateScriptTypeAttribute);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(typeof(FakeGenerateScriptTypeAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

            FakeGenerateScriptTypeAttribute fgsta = attrs[0] as FakeGenerateScriptTypeAttribute;
            Assert.AreEqual("ScriptName", fgsta.Name);
        }

        // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032
        // should not fail
        // Not fixed by .NET 2.0 SP1.
        [Test]
        public void ProxySpecificTargetTypeAttributeWithPublicEnumProperty()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithPublicEnumPropertyAttribute);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(typeof(PublicEnumPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

            PublicEnumPropertyAttribute pepa = attrs[0] as PublicEnumPropertyAttribute;
            Assert.AreEqual(AttributeTargets.All, pepa.Data);
        }

#if !NETCOREAPP
        // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522
        // should not fail
        // Use CustomAttributeData if patch applied (.NET 2.0 SP1).
        // Use Attribute instance if patch not applied (.NET 2.0 SP1).
        [Test]
        public void ProxyWebServiceAttribute()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithWebServiceAttribute);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(typeof(System.Web.Services.WebServiceAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

            var wsa = attrs[0] as System.Web.Services.WebServiceAttribute;
            Assert.AreEqual("blah", wsa.Name);
            Assert.AreEqual("http://mynamespace.com", wsa.Namespace);
        }
#endif

#if !MONO && !NETCOREAPP
        // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803
        [Test]
        [Platform("Win")]
        public void ProxySecurityAttribute()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithSecurityAttribute);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 2 attribute applied to the target type.");
            Assert.AreEqual(2, attrs.Length, "Should have had 2 attribute applied to the target type.");
        }
#endif

        [Test]
        public void ProxySpecificTargetTypeAttributeWithArrayConstructor()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithArrayConstructorAttribute);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(typeof(ArrayConstructorPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

            ArrayConstructorPropertyAttribute acpa = attrs[0] as ArrayConstructorPropertyAttribute;
            Assert.AreEqual(false, acpa.ReadOnly);
            Assert.AreEqual(1, acpa.Types.Length);
            Assert.AreEqual(typeof(string), acpa.Types[0]);
        }

        [Test]
        public void ProxySpecificTargetTypeAttributeWithArrayProperty()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithArrayPropertyAttribute);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(typeof(ArrayConstructorPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

            ArrayConstructorPropertyAttribute acpa = attrs[0] as ArrayConstructorPropertyAttribute;
            Assert.AreEqual(true, acpa.ReadOnly);
            Assert.AreEqual(2, acpa.Types.Length);
            Assert.AreEqual(typeof(int), acpa.Types[0]);
            Assert.AreEqual(typeof(string), acpa.Types[1]);
        }

        [Test]
        public void ProxyTargetTypeAttributes()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(AnotherMarkerClass);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");
        }

        [Test]
        public void DoesNotProxyTargetTypeAttributesWithProxyTargetAttributesEqualsFalse()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(AnotherMarkerClass);
            builder.ProxyTargetAttributes = false;
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            object[] attrs = proxy.GetCustomAttributes(false);
            Assert.IsNotNull(attrs);
            Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target type.");
        }

        [Test]
        public void ProxyTargetMethodAttributes()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(AnotherMarkerClass);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");
        }

        [Test]
        public void DoesNotProxyTargetMethodAttributesWithProxyTargetAttributesEqualsFalse()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(AnotherMarkerClass);
            builder.ProxyTargetAttributes = false;
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetCustomAttributes(false);
            Assert.IsNotNull(attrs);
            Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target method.");
        }

        [Test]
        public void ProxyTargetMethodParameterAttributes()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(SomeMarkerClass);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetParameters()[1].GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's parameter.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's parameter.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter.");
        }

        [Test]
        [Description("SPRNET-1134")]
        public void ProxyTargetMethodParameterMultipleAttributes()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(MultipleMarkerClass);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetParameters()[1].GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 2 attributes applied to the method's parameter.");
            Assert.AreEqual(2, attrs.Length, "Should have had 2 attributes applied to the method's parameter.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[1].GetType(), "Wrong System.Type of Attribute applied to the method's parameter.");
        }

        [Test]
        public void DoesNotProxyTargetMethodParameterAttributesWithProxyTargetAttributesEqualsFalse()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(SomeMarkerClass);
            builder.ProxyTargetAttributes = false;
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.GetParameters()[1].GetCustomAttributes(false);
            Assert.IsNotNull(attrs);
            Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's parameter.");
        }

        [Test]
        public void ProxyTargetMethodReturnValueAttributes()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(SomeMarkerClass);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's return value.");
            Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's return value.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value.");
        }

        [Test]
        [Description("SPRNET-1134")]
        public void ProxyTargetMethodReturnValueMultipleAttributes()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(MultipleMarkerClass);
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false);
            Assert.IsNotNull(attrs, "Should have had 2 attribute applied to the method's return value.");
            Assert.AreEqual(2, attrs.Length, "Should have had 2 attribute applied to the method's return value.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value.");
            Assert.AreEqual(typeof(MarkerAttribute), attrs[1].GetType(), "Wrong System.Type of Attribute applied to the method's return value.");
        }

        [Test]
        public void DoesNotProxyTargetMethodReturnValueAttributesWithProxyTargetAttributesEqualsFalse()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(SomeMarkerClass);
            builder.ProxyTargetAttributes = false;
            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
            MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
            if (method == null)
            {
                method = proxy.GetMethod("MarkerMethod");
            }
            Assert.IsNotNull(method);
            object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false);
            Assert.IsNotNull(attrs);
            Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's return value.");
        }

        [Test]
        public void ProxyGenericMethod()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassWithGenericMethod);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            InterfaceWithGenericMethod foo = Activator.CreateInstance(proxy) as InterfaceWithGenericMethod;
            Assert.IsNotNull(foo);

            foo.PolymorphicMethod<int>();

            string result1 = foo.PolymorphicMethod("coucou");
            Assert.AreEqual("coucou", result1);

            foo.WithGenericParameter<string>();

            foo.WithGenericParameterAndGenericArgument<string>("ola");

            string result2 = foo.WithGenericParameterAndGenericArgumentAndGenericReturnType<string>("ola");
            Assert.AreEqual("ola", result2);

            foo.WithInterfaceConstraint<bool>();

            foo.WithBaseTypeConstraint<DerivedTestObject>();

            foo.WithBaseTypeAndInterfaceConstraints<DerivedTestObject>();

            foo.WithMixedConstraint<bool, DerivedTestObject>();
        }

        public interface InterfaceWithGenericMethod
        {
            void PolymorphicMethod<T>();

            string PolymorphicMethod(string param);

            void WithGenericParameter<T>();

            void WithGenericParameterAndGenericArgument<T>(T obj);

            T WithGenericParameterAndGenericArgumentAndGenericReturnType<T>(T obj);

            void WithInterfaceConstraint<Z>() where Z : IComparable;

            void WithBaseTypeConstraint<Z>() where Z : TestObject;

            void WithBaseTypeAndInterfaceConstraints<Z>() where Z : TestObject, IComparable, IDisposable;

            void WithMixedConstraint<X, Y>()
                where X : IComparable, new()
                where Y : TestObject;
        }

        public class ClassWithGenericMethod : InterfaceWithGenericMethod
        {
            public void PolymorphicMethod<T>() { }

            public string PolymorphicMethod(string param)
            {
                return param;
            }

            public void WithGenericParameter<T>() { }

            public void WithGenericParameterAndGenericArgument<T>(T obj) { }

            public T WithGenericParameterAndGenericArgumentAndGenericReturnType<T>(T obj)
            {
                return obj;
            }

            public void WithInterfaceConstraint<Z>() where Z : IComparable { }

            public void WithBaseTypeConstraint<Z>() where Z : TestObject { }

            public void WithBaseTypeAndInterfaceConstraints<Z>() where Z : TestObject, IComparable, IDisposable { }

            public void WithMixedConstraint<X, Y>()
                where X : IComparable, new()
                where Y : TestObject
            { }
        }

        [Test]
        public void ProxyGenericInterface()
        {
            IProxyTypeBuilder builder = GetProxyBuilder();
            builder.TargetType = typeof(ClassThatImplementsGenericInterface<TestObject>);

            Type proxy = builder.BuildProxyType();
            Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

            GenericInterface<TestObject> foo = Activator.CreateInstance(proxy) as GenericInterface<TestObject>;
            Assert.IsNotNull(foo);

            TestObject to1 = foo.Create();
            Assert.IsNotNull(to1);

            TestObject to2 = foo.Populate(new TestObject());
            Assert.IsNotNull(to2);
            Assert.AreEqual("Populated", to2.Name);
        }

        public interface GenericInterface<T>
            where T : ITestObject, new()
        {
            T Create();

            T Populate(T testObject);
        }

        public class ClassThatImplementsGenericInterface<T> : GenericInterface<T>
            where T : ITestObject, new()
        {
            public T Create()
            {
                return new T();
            }

            public T Populate(T testObject)
            {
                testObject.Name = "Populated";

                return testObject;
            }
        }

        protected abstract IProxyTypeBuilder GetProxyBuilder();

        public interface InnerInterface
        {
        }

        public class InnerClass : InnerInterface
        {
        }
    }

    public class DoesntImplementAnyInterfaces
    {
    }

    public interface IMarkerInterface
    {
    }

    public class MarkerClass : IMarkerInterface
    {
    }

    [GuidAttribute("7cfc9607-e81a-48ac-a080-bda88193607b")]
    public class ClassWithGuidAttribute : IMarkerInterface
    {
    }

    [Marker]
    public interface IAnotherMarkerInterface
    {
        [Marker]
        void MarkerMethod();
    }

    public interface ISomeMarkerInterface
    {
        string MarkerMethod(int param1, string param2);
    }

    // XmlElementAttribute.Order property is only available in .NET 2.0
    public sealed class FakeXmlElementAttribute : Attribute
    {
        private int _order;
        public int Order
        {
            get
            {
                return this._order;
            }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("Negative values are prohibited.", "Order");
                }
                this._order = value;
            }
        }

        private string _elementName;
        public string ElementName
        {
            get { return _elementName; }
            set { _elementName = value; }
        }

        public FakeXmlElementAttribute()
        {
            this._order = -1;
        }

        public FakeXmlElementAttribute(string elementName)
        {
            this._order = -1;
            this._elementName = elementName;
        }
    }

    public sealed class FakeGenerateScriptTypeAttribute : Attribute
    {
        public FakeGenerateScriptTypeAttribute(string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            this._name = name;
        }

        private string _name;
        public string Name
        {
            get
            {
                return this._name;
            }
        }
    }

    public sealed class FakeServiceKnownTypeAttribute : Attribute
    {
        private Type declaringType;
        private string methodName;
        private Type type;

        private FakeServiceKnownTypeAttribute()
        {
        }

        public FakeServiceKnownTypeAttribute(string methodName)
        {
            this.methodName = methodName;
        }

        public FakeServiceKnownTypeAttribute(Type type)
        {
            this.type = type;
        }

        public FakeServiceKnownTypeAttribute(string methodName, Type declaringType)
        {
            this.methodName = methodName;
            this.declaringType = declaringType;
        }

        public Type DeclaringType
        {
            get
            {
                return this.declaringType;
            }
        }

        public string MethodName
        {
            get
            {
                return this.methodName;
            }
        }

        public Type Type
        {
            get
            {
                return this.type;
            }
        }
    }

    public class ClassWithFakeXmlElementAttribute : ISomeMarkerInterface
    {
        public string MarkerMethod([FakeXmlElement("parameter1")] int param1, string param2)
        {
            return null;
        }
    }

#if !NETCOREAPP
    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522
    [System.Web.Services.WebService(Namespace = "http://mynamespace.com", Name = "blah")]
    public class ClassWithWebServiceAttribute : IMarkerInterface
    {
    }
#endif

    [FakeGenerateScriptType("ScriptName")]
    public class ClassWithFakeGenerateScriptTypeAttribute : IMarkerInterface
    {
    }

    //[FakeServiceKnownTypeAttribute(typeof(TestObject))]
    public class ClassWithFakeServiceKnownTypeAttribute : IMarkerInterface
    {
    }

    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032
    public class ClassWithPublicEnumPropertyAttribute : ISomeMarkerInterface
    {
        [PublicEnumProperty(AttributeTargets.All)]
        public string MarkerMethod(int param1, string param2)
        {
            return null;
        }
    }

#if !NETCOREAPP
    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803
    [Marker]
#pragma warning disable 618
    [System.Net.WebPermission(System.Security.Permissions.SecurityAction.Deny)]
#pragma warning restore 618
    public class ClassWithSecurityAttribute : IMarkerInterface
    {
    }
#endif

    public class ClassWithArrayConstructorAttribute : ISomeMarkerInterface
    {
        [ArrayConstructorPropertyAttribute(false, new Type[1] { typeof(string) })]
        public string MarkerMethod(int param1, string param2)
        {
            return null;
        }
    }

    public class ClassWithArrayPropertyAttribute : IAnotherMarkerInterface
    {
        [ArrayConstructorPropertyAttribute(ReadOnly = true, Types = new Type[2] { typeof(int), typeof(string) })]
        public void MarkerMethod()
        {
        }
    }

    public class SomeMarkerClass : ISomeMarkerInterface
    {
        [return: Marker]
        public string MarkerMethod(int param1, [Marker]string param2)
        {
            return string.Empty;
        }

        [return: Marker]
        public virtual string MarkerVirtualMethod(int param1, [Marker]string param2)
        {
            return string.Empty;
        }
    }

    // SPRNET-1134
    public class MultipleMarkerClass : ISomeMarkerInterface
    {
        [return: Marker(Count = 0)]
        [return: Marker(Count = 1)]
        public string MarkerMethod(int param1, [Marker(Count = 0)][Marker(Count = 1)]string param2)
        {
            return string.Empty;
        }

        [return: Marker(Count = 0)]
        [return: Marker(Count = 1)]
        public virtual string MarkerVirtualMethod(int param1, [Marker(Count = 0)][Marker(Count = 1)]string param2)
        {
            return string.Empty;
        }
    }

    [Marker]
    public class AnotherMarkerClass : IAnotherMarkerInterface
    {
        [Marker]
        public void MarkerMethod()
        {
        }

        [Marker]
        public virtual void MarkerVirtualMethod()
        {
        }
    }

    public class ArrayConstructorPropertyAttribute : Attribute
    {
        private bool _readOnly = false;
        private Type[] _types = Type.EmptyTypes;

        public ArrayConstructorPropertyAttribute()
        {
        }

        public ArrayConstructorPropertyAttribute(bool readOnly, Type[] types)
        {
            this._readOnly = readOnly;
            this._types = types;
        }

        public bool ReadOnly
        {
            get { return _readOnly; }
            set { _readOnly = value; }
        }

        public Type[] Types
        {
            get { return _types; }
            set { _types = value; }
        }
    }

    public class PublicEnumPropertyAttribute : Attribute
    {
        private Enum _data;

        public PublicEnumPropertyAttribute(AttributeTargets data)
        {
            _data = data;
        }

        public PublicEnumPropertyAttribute(TypeCode data)
        {
            _data = data;
        }

        public Enum Data
        {
            get { return _data; }
        }
    }

    [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
    public sealed class MarkerAttribute : Attribute
    {
        private int _count;

        public int Count
        {
            get { return _count; }
            set { _count = value; }
        }
    }

    public interface IBase
    {
        void Base();
    }

    public interface IInherited : IBase
    {
        void Inherited();
    }

    public class MultipleInterfaces : IInherited
    {
        public void Inherited()
        {
        }

        public void Base()
        {
        }
    }

    public interface ITargetObjectTest
    {
        string InterfaceProperty { get; set; }

        bool InterfaceMethodWithArguments(string code);

        void InterfaceVirtualMethodWithNoArguments();
    }

    public class TargetObjectTest : ITargetObjectTest
    {
        private string _interfaceProperty;

        public TargetObjectTest() { }

        public TargetObjectTest(string interfaceProperty)
        {
            _interfaceProperty = interfaceProperty;
        }

        public virtual string InterfaceProperty
        {
            get { return _interfaceProperty; }
            set { _interfaceProperty = value; }
        }

        public bool InterfaceMethodWithArguments(string code)
        {
            return true;
        }

        public virtual void InterfaceVirtualMethodWithNoArguments()
        {
        }

        public void MethodWithArguments(string code)
        {
        }
    }

    [ProxyIgnore]
    public interface IFrameworkInterface
    {
        void FrameworkMethod();
    }

    public interface IApplicationInterface
    {
        void ApplicationMethod();
    }

    public class ApplicationClass : IApplicationInterface, IFrameworkInterface
    {
        public void FrameworkMethod() { }

        public void ApplicationMethod() { }
    }
}